package tms.unitTests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;

import org.junit.Before;
import org.junit.Test;

import tms.model.PeopleList;
import tms.model.Person;
import tms.model.Task;

/**
 * PeopleListUnitTests class contains the units tests of the methods in PeopleList class 
 * 
 * @author Amer Alhalabi
 *
 */
public class PeopleListUnitTests {

	/// a header line used in converting a task to a string
	final String H_LINE = "---------------------------------------";
	/// a header line used in writing the people table to a csv file
	private static final String HEADER_STRING = "Name,Hours,Tasks\n";
	
	/**
	 * default constructor
	 */
	public PeopleListUnitTests() {
    }
	
	/**
	 * helper method to write a string to a file
	 * 
	 * @param fileName the name of the file to be written to 
	 * @param content content to be writen to the file
	 */
	private void createFile( String fileName, String content) {
		PrintWriter printOut = null; 
		try {
			printOut = new PrintWriter(fileName); 
			printOut.print(content); 
			printOut.close();
		}
		catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
		}
	}
	
	/**
	 * helper method to check if a file exists
	 * 
	 * @param fileName name of the file to be checked 
	 * 
	 * @return true if the file exists; false otherwise
	 */
	private boolean fileExists(String fileName) {
		if (fileName == null || fileName == "")
			return false;
		File file = new File(fileName);
		return file.exists();
	}
	
	/**
	 * helper method to delete a file
	 * 
	 * @param fileName the name of the file to be deleted
	 */
	private void deleteFile(String fileName) {
		File file = new File(fileName);
		file.delete();
	}
	
	/**
	 * helper method to load the content of a file into a string
	 * 
	 * @param filename name of the file whose content is to be loaded
	 * 
	 * @return a string containing the content of the file 
	 */
	private String loadFileContent(String filename) {
		byte[] buffer = new byte[(int) new File(filename).length()];
	    BufferedInputStream bufferedInStream = null;
	    try {
	    	bufferedInStream = new BufferedInputStream(new FileInputStream(filename));
	    	bufferedInStream.read(buffer);
	    } catch (IOException e) {
			e.printStackTrace();
		} finally {
	        if (bufferedInStream != null) try { bufferedInStream.close(); } catch (IOException ignored) { }
	    }
	    return new String(buffer);
	}
	
	/**
	 * helper method called before every unit test to clear the peopleList
	 */
	@Before
	public void setUp() {
		PeopleList.getInstance().getPeopleList().clear();
	}
	
	/**
	 * test that the only instance of the people table is not null 
	 * and that it contains no person object
	 */
	@Test
	public void testPeopleListGetInstance1() {
       
		PeopleList peopleList = PeopleList.getInstance();
		assertNotNull(peopleList);
		assertTrue(peopleList.getPeopleList().isEmpty());
    }
	
	/**
	 * test that the getInstance() always returns the same instance of the peopleList no matter how
	 * many times it is called
	 */
	@Test
	public void testPeopleListGetInstance2() {
       
		PeopleList peopleList1 = PeopleList.getInstance();
		PeopleList peopleList2 = PeopleList.getInstance();
		PeopleList peopleList3 = PeopleList.getInstance();
		
		assertNotNull(peopleList1);
		assertSame(peopleList1, peopleList2);
		assertSame(peopleList2, peopleList3);
    }

	/**
	 *  test saving people table to an csv file where the filename is null
	 *  expect that no output file gets created
	 */
	@Test
	public void testPeopleListSaveCSVFile1() {
		
		String filename = null;
		PeopleList peopleList = PeopleList.getInstance();
		
		assertFalse(peopleList.saveCSVFile(filename));
		assertFalse(fileExists(filename));
	}
	
	/**
	 *  test saving people table to an csv file where the filename is empty
	 *  expect that no output file gets created
	 */
	@Test
	public void testPeopleListSaveCSVFile2() {
		
		String filename = "";
		PeopleList peopleList = PeopleList.getInstance(); 
		
		assertFalse(peopleList.saveCSVFile(filename));
		assertFalse(fileExists(filename));
	}
	
	/**
	 *  test saving people table to an csv file that already exists
	 *  expect the content file to be overwritten 
	 */
	@Test
	public void testPeopleListSaveCSVFile3() {
		Calendar deadline = Calendar.getInstance();
		
		// output filename
		String outFilename = "out.csv";

		Task task1 = new Task();
		task1.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task1.setDeadline(deadline.getTime());
		task1.setDeliverable("del1");
		task1.setDescription("des1");
		task1.setDuration(1);
		task1.setId(1);
		task1.setPerson("Person 1");
		task1.setTitle("Iteration 1");
		
		Task task2 = new Task();
		task2.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task2.setDeadline(deadline.getTime());
		task2.setDeliverable("del2");
		task2.setDescription("des2");
		task2.setDuration(2);
		task2.setId(2);
		task2.setPerson("Person 2");
		task2.setTitle("Iteration 2");
		
		Task task3 = new Task();
		task3.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task3.setDeadline(deadline.getTime());
		task3.setDeliverable("del3");
		task3.setDescription("des3");
		task3.setDuration(3);
		task3.setId(3);
		task3.setPerson("Person 1");
		task3.setTitle("Iteration 3");
		
		Person person1 = new Person();
		person1.setName("Person 1");
		person1.addTask(task1);
		person1.addTask(task3);
		
		Person person2 = new Person();
		person2.setName("Person 2");
		person2.addTask(task2);
		
		// create the output file
		createFile(outFilename, "");

		PeopleList peopleList = PeopleList.getInstance();
		peopleList.addPerson(person1);
		peopleList.addPerson(person2);

		// expect that two people were added to the people list 
		// as there are two tasks that are assigned to the person
		assertEquals(peopleList.getPeopleList().size(), 2);
		
		// save the people list to an CSV file
		// and expect successful operation
		assertTrue(peopleList.saveCSVFile(outFilename));
		assertTrue(fileExists(outFilename));
		
		// compare the new content of the people CSV file with the expected one  
		String cont = loadFileContent(outFilename);
		String expected = "";
		expected += HEADER_STRING ;
		expected += "Person 1,32,\"1, 3\""; 
		expected += "\n";
		expected += "Person 2,16,\"2\""; 
		expected += "\n";
		assertTrue(cont.equals(expected));
		
		// clean up
		deleteFile(outFilename);
	}
	
	/**
	 *  test saving an empty people table to an csv file
	 *  expect that a csv file with only the file header will be created 
	 */
	@Test
	public void testPeopleListSaveCSVFile4() {
		
		String filename = "test.csv";
		
		// this is an empty people table
		PeopleList peopleList = PeopleList.getInstance();
		
		// test output csv file contains only the header string
		assertTrue(peopleList.saveCSVFile(filename));
		assertTrue(fileExists(filename));
		String cont = loadFileContent(filename);
		assertTrue(cont.equals(HEADER_STRING));
		
		// clean up
		deleteFile(filename);
	}

	/**
	 *  test saving an people table that contains two person objects to an csv file
	 *  expect that an CSV file to be created with valid content
	 */
	@Test
	public void testPeopleListSaveCSVFile5() {
		
		Calendar deadline = Calendar.getInstance();
		
		// output filename
		String outFilename = "out.csv";

		Task task1 = new Task();
		task1.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task1.setDeadline(deadline.getTime());
		task1.setDeliverable("del1");
		task1.setDescription("des1");
		task1.setDuration(1);
		task1.setId(1);
		task1.setPerson("Person 1");
		task1.setTitle("Iteration 1");
		
		Task task2 = new Task();
		task2.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task2.setDeadline(deadline.getTime());
		task2.setDeliverable("del2");
		task2.setDescription("des2");
		task2.setDuration(2);
		task2.setId(2);
		task2.setPerson("Person 2");
		task2.setTitle("Iteration 2");
		
		Task task3 = new Task();
		task3.setBeforeId(-1);
		deadline.set(2012, 1, 27);
		task3.setDeadline(deadline.getTime());
		task3.setDeliverable("del3");
		task3.setDescription("des3");
		task3.setDuration(3);
		task3.setId(3);
		task3.setPerson("Person 1");
		task3.setTitle("Iteration 3");
		
		Person person1 = new Person();
		person1.setName("Person 1");
		person1.addTask(task1);
		person1.addTask(task3);
		
		Person person2 = new Person();
		person2.setName("Person 2");
		person2.addTask(task2);
		
		PeopleList peopleList = PeopleList.getInstance();
		peopleList.addPerson(person1);
		peopleList.addPerson(person2);

		// expect that two people were added to the people list 
		// as there are two tasks that are assigned to the person
		assertEquals(peopleList.getPeopleList().size(), 2);

		// expect the output CSV people file to exist 
		// and verify its content
		assertFalse(fileExists(outFilename));
		assertTrue(peopleList.saveCSVFile(outFilename));
		assertTrue(fileExists(outFilename));
		
		String cont = loadFileContent(outFilename);
		String expected = "";
		expected += HEADER_STRING ;
		expected += "Person 1,32,\"1, 3\""; 
		expected += "\n";
		expected += "Person 2,16,\"2\""; 
		expected += "\n";
		assertTrue(cont.equals(expected));
		
		// clean up
		deleteFile(outFilename);
	}
	
	/**
	 * clear an empty PeopleList
	 */
	@Test
	public void testPeopleListClear1() {
		PeopleList peopleList = PeopleList.getInstance();
		
		peopleList.clear();
		assertEquals(0, peopleList.getPeopleList().size());
	}

	/**
	 * clear a non-empty empty PeopleList
	 */
	@Test
	public void testPeopleListClear2() {
		PeopleList peopleList = PeopleList.getInstance();
		
		Person person1 = new Person();
		Person person2 = new Person();
		Person person3 = new Person();

		peopleList.addPerson(person1);
		peopleList.addPerson(person2);
		peopleList.addPerson(person3);

		peopleList.clear();
		assertEquals(0, peopleList.getPeopleList().size());
	}
	
	/**
	 * test adding person to PeopleList when person is null
	 */
	@Test
	public void testPeopleListAddPerson1() {
		
		PeopleList peopleList = PeopleList.getInstance();
		
		assertEquals(0, peopleList.getPeopleList().size());
		peopleList.addPerson(null);
		assertEquals(0, peopleList.getPeopleList().size());
	}
	
	/**
	 * test adding one person to PeopleList
	 */
	@Test
	public void testPeopleListAddPerson2() {
		PeopleList peopleList = PeopleList.getInstance();

		assertEquals(0, peopleList.getPeopleList().size());
		
		Person person = new Person();
		peopleList.addPerson(person);
		
		assertEquals(1, peopleList.getPeopleList().size());
	}
	
	/**
	 * test adding the same person to PeopleList twice
	 */
	@Test
	public void testPeopleListAddPerson3() {
		
		PeopleList peopleList = PeopleList.getInstance();

		assertEquals(0, peopleList.getPeopleList().size());
		
		Person person = new Person();
		peopleList.addPerson(person);
		assertEquals(1, peopleList.getPeopleList().size());
		
		peopleList.addPerson(person);
		assertEquals(1, peopleList.getPeopleList().size());
	}
	
	/**
	 * test adding three different persons to PeopleList
	 */
	@Test
	public void testPeopleListAddPerson4() {
		
		PeopleList peopleList = PeopleList.getInstance();
		
		Person person1 = new Person();
		person1.setName("Person 1");
		Person person2 = new Person();
		person2.setName("Person 2");
		Person person3 = new Person();
		person3.setName("Person 3");

		assertEquals(0, peopleList.getPeopleList().size());
		
		peopleList.addPerson(person1);
		peopleList.addPerson(person2);
		peopleList.addPerson(person3);

		assertEquals(3, peopleList.getPeopleList().size());
	}

}
